﻿namespace MobileTrack.Web.Tests.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Principal;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    using System.Web.Security;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using MobileTrack.Core;
    using MobileTrack.Core.Data;
    using MobileTrack.Core.Model;
    using MobileTrack.Web.Controllers;
    using MobileTrack.Web.ViewModels;
    using Moq;

    [TestClass]
    public class HomeControllerFixture
    {
        private IList<Celular> celulares;
        private string userRole;
        private bool isInRole;

        private Mock<IRepository<Celular>> celularesRepositoryMock;
        private Mock<IRepositories> repositoriesMock;
        private Mock<IPrincipal> principalMock;

        [TestInitialize]
        public void TestInitialize()
        {
            this.celulares = new List<Celular>();
            this.userRole = string.Empty;
            this.isInRole = false;

            this.celularesRepositoryMock = new Mock<IRepository<Celular>>();
            this.celularesRepositoryMock.Setup(cr => cr.All).Returns(() => this.celulares.AsQueryable());

            this.repositoriesMock = new Mock<IRepositories>();
            this.repositoriesMock.Setup(rs => rs.Celulares).Returns(() => this.celularesRepositoryMock.Object);

            this.principalMock = new Mock<IPrincipal>();
            this.principalMock.Setup(p => p.IsInRole(It.Is<string>(r => r.Equals(this.userRole, StringComparison.OrdinalIgnoreCase)))).Returns(() => this.isInRole);
        }

        [TestMethod]
        public void IndexShouldReturnNoNotificationsForAdministrator()
        {
            this.userRole = Constants.AdministradorRole;
            this.isInRole = true;

            var controller = this.CreateHomeController();

            var result = controller.Index() as ViewResult;
            Assert.IsNotNull(result);

            var model = result.Model as NotificationsViewModel;
            Assert.IsNotNull(model);
            Assert.AreEqual(0, model.Notifications.Count);

            this.VerifyAll();
        }

        [TestMethod]
        public void IndexShouldReturnAllNotificationsForAdministrator()
        {
            var sucursal = "Sucursal";

            this.userRole = Constants.AdministradorRole;
            this.isInRole = true;
            this.celulares = new List<Celular>
            {
                this.CreateCelular(Estado.Pendiente, sucursal),
                this.CreateCelular(Estado.EnDeposito, sucursal),
                this.CreateCelular(Estado.EnTransito, sucursal),
                this.CreateCelular(Estado.DisponibleVenta, sucursal),
                this.CreateCelular(Estado.Vendido, sucursal),
                this.CreateCelular(Estado.Dañado, sucursal)
            };

            var controller = this.CreateHomeController();

            var result = controller.Index() as ViewResult;
            Assert.IsNotNull(result);

            var model = result.Model as NotificationsViewModel;
            Assert.IsNotNull(model);
            Assert.AreEqual(6, model.Notifications.Count);

            this.VerifyAll();
        }

        [TestMethod]
        public void IndexShouldReturnNotificationsForAsocianteAndSucursal()
        {
            var sucursal = "Sucursal";
            var userName = "Asociante";

            this.userRole = Constants.AsocianteRole;
            this.isInRole = true;
            this.celulares = new List<Celular>
            {
                this.CreateCelular(Estado.Pendiente, sucursal),
                this.CreateCelular(Estado.EnDeposito, sucursal),
                this.CreateCelular(Estado.EnTransito, sucursal),
                this.CreateCelular(Estado.DisponibleVenta, sucursal),
                this.CreateCelular(Estado.Vendido, sucursal),
                this.CreateCelular(Estado.Dañado, sucursal)
            };

            var identityMock = new Mock<IIdentity>();
            identityMock.Setup(i => i.Name).Returns(userName);

            this.principalMock.Setup(p => p.Identity).Returns(identityMock.Object);

            var roleProviderMock = new Mock<RoleProvider>();
            roleProviderMock.Setup(rp => rp.GetRolesForUser(userName)).Returns(new[] { string.Concat(sucursal, Constants.SucursalRoleSuffix) });

            var controller = this.CreateHomeController(roleProvider: roleProviderMock.Object);

            var result = controller.Index() as ViewResult;
            Assert.IsNotNull(result);

            var model = result.Model as NotificationsViewModel;
            Assert.IsNotNull(model);
            Assert.AreEqual(3, model.Notifications.Count);

            this.VerifyAll();
            identityMock.VerifyAll();
            roleProviderMock.VerifyAll();
        }

        [TestMethod]
        public void IndexShouldReturnNotificationsForDespachanteAndSucursal()
        {
            var sucursal = "Sucursal";
            var userName = "Despachante";

            this.userRole = Constants.DespachanteRole;
            this.isInRole = true;
            this.celulares = new List<Celular>
            {
                this.CreateCelular(Estado.Pendiente, sucursal),
                this.CreateCelular(Estado.EnDeposito, sucursal),
                this.CreateCelular(Estado.EnTransito, sucursal),
                this.CreateCelular(Estado.DisponibleVenta, sucursal),
                this.CreateCelular(Estado.Vendido, sucursal),
                this.CreateCelular(Estado.Dañado, sucursal)
            };

            var identityMock = new Mock<IIdentity>();
            identityMock.Setup(i => i.Name).Returns(userName);

            this.principalMock.Setup(p => p.Identity).Returns(identityMock.Object);

            var roleProviderMock = new Mock<RoleProvider>();
            roleProviderMock.Setup(rp => rp.GetRolesForUser(userName)).Returns(new[] { string.Concat(sucursal, Constants.SucursalRoleSuffix) });

            var controller = this.CreateHomeController(roleProvider: roleProviderMock.Object);

            var result = controller.Index() as ViewResult;
            Assert.IsNotNull(result);

            var model = result.Model as NotificationsViewModel;
            Assert.IsNotNull(model);
            Assert.AreEqual(3, model.Notifications.Count);

            this.VerifyAll();
            identityMock.VerifyAll();
            roleProviderMock.VerifyAll();
        }

        [TestMethod]
        public void IndexShouldReturnNotificationsForReceptorAndSucursal()
        {
            var sucursal = "Sucursal";
            var userName = "Receptor";

            this.userRole = Constants.ReceptorRole;
            this.isInRole = true;
            this.celulares = new List<Celular>
            {
                this.CreateCelular(Estado.Pendiente, sucursal),
                this.CreateCelular(Estado.EnDeposito, sucursal),
                this.CreateCelular(Estado.EnTransito, sucursal),
                this.CreateCelular(Estado.DisponibleVenta, sucursal),
                this.CreateCelular(Estado.Vendido, sucursal),
                this.CreateCelular(Estado.Dañado, sucursal)
            };

            var identityMock = new Mock<IIdentity>();
            identityMock.Setup(i => i.Name).Returns(userName);

            this.principalMock.Setup(p => p.Identity).Returns(identityMock.Object);

            var roleProviderMock = new Mock<RoleProvider>();
            roleProviderMock.Setup(rp => rp.GetRolesForUser(userName)).Returns(new[] { string.Concat(sucursal, Constants.SucursalRoleSuffix) });

            var controller = this.CreateHomeController(roleProvider: roleProviderMock.Object);

            var result = controller.Index() as ViewResult;
            Assert.IsNotNull(result);

            var model = result.Model as NotificationsViewModel;
            Assert.IsNotNull(model);
            Assert.AreEqual(3, model.Notifications.Count);

            this.VerifyAll();
            identityMock.VerifyAll();
            roleProviderMock.VerifyAll();
        }

        [TestMethod]
        public void IndexShouldReturnNotificationsForVendedorAndSucursal()
        {
            var sucursal = "Sucursal";
            var userName = "Vendedor";

            this.userRole = Constants.VendedorRole;
            this.isInRole = true;
            this.celulares = new List<Celular>
            {
                this.CreateCelular(Estado.Pendiente, sucursal),
                this.CreateCelular(Estado.EnDeposito, sucursal),
                this.CreateCelular(Estado.EnTransito, sucursal),
                this.CreateCelular(Estado.DisponibleVenta, sucursal),
                this.CreateCelular(Estado.Vendido, sucursal),
                this.CreateCelular(Estado.Dañado, sucursal)
            };

            var identityMock = new Mock<IIdentity>();
            identityMock.Setup(i => i.Name).Returns(userName);

            this.principalMock.Setup(p => p.Identity).Returns(identityMock.Object);

            var roleProviderMock = new Mock<RoleProvider>();
            roleProviderMock.Setup(rp => rp.GetRolesForUser(userName)).Returns(new[] { string.Concat(sucursal, Constants.SucursalRoleSuffix) });

            var controller = this.CreateHomeController(roleProvider: roleProviderMock.Object);

            var result = controller.Index() as ViewResult;
            Assert.IsNotNull(result);

            var model = result.Model as NotificationsViewModel;
            Assert.IsNotNull(model);
            Assert.AreEqual(4, model.Notifications.Count);

            this.VerifyAll();
            identityMock.VerifyAll();
            roleProviderMock.VerifyAll();
        }

        private HomeController CreateHomeController(MembershipProvider membershipProvider = null, RoleProvider roleProvider = null)
        {
            var httpContextMock = new Mock<HttpContextBase>();
            httpContextMock.Setup(c => c.User).Returns(() => this.principalMock.Object);
            httpContextMock.Setup(c => c.Request).Returns(new Mock<HttpRequestBase>().Object);
            httpContextMock.Setup(c => c.Response).Returns(new Mock<HttpResponseBase>().Object);

            var httpContext = httpContextMock.Object;

            var routes = new RouteCollection();
            RouteConfig.RegisterRoutes(routes);

            var controller = new HomeController(this.repositoriesMock.Object, membershipProvider, roleProvider)
            {
                ControllerContext = new ControllerContext { HttpContext = httpContext },
                Url = new UrlHelper(new RequestContext(httpContext, new RouteData()), routes),
            };

            return controller;
        }

        private void VerifyAll()
        {
            this.celularesRepositoryMock.VerifyAll();
            this.repositoriesMock.VerifyAll();
            this.principalMock.VerifyAll();
        }

        private Celular CreateCelular(Estado lastEstado, string lastSucursalName)
        {
            var today = DateTime.Today;
            var celular = new Celular();
            celular.Seguimientos.Add(new Seguimiento { Id = 1, Estado = Estado.Pendiente, Fecha = today, Sucursal = new Sucursal { Nombre = "SampleSucName1", Codigo = "SampleSucCod1" } });
            celular.Seguimientos.Add(new Seguimiento { Id = 2, Estado = lastEstado, Fecha = today, Sucursal = new Sucursal { Nombre = lastSucursalName, Codigo = "SampleSucCod2" } });

            return celular;
        }
    }
}
